package world.snowcrystal.service.Impl;

import jakarta.annotation.Nonnull;
import lombok.extern.log4j.Log4j2;
import org.redisson.api.RFuture;
import org.redisson.api.RList;
import org.redisson.api.RMapCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import world.snowcrystal.dto.ChatroomMessage;
import world.snowcrystal.dto.ChatroomUser;
import world.snowcrystal.exception.SnowcrystalAlreadyExistException;
import world.snowcrystal.service.ChatroomService;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

@Log4j2
@Service
public class RedissonBasedChatroomService implements ChatroomService {


    private RList<ChatroomMessage> messageCache;


    private RMapCache<String, ChatroomUser> userCache;


    @Autowired
    public RedissonBasedChatroomService(
            @Qualifier("chatroomOnlineUserCache")
            @Nonnull RMapCache<String, ChatroomUser> userCache,
            @Qualifier("redisChatroomMessageCache")
            @Nonnull RList<ChatroomMessage> messageCache) {
        this.userCache = userCache;
        this.messageCache = messageCache;
    }


    /**
     * 获取当前聊天室在线人数，包括所有匿名用户在内
     *
     * @return long 并非 100% 保证正确
     * @author tianqing
     * @since 1.0
     */
    @Override
    public int getOnlineUsers() {
        return userCache.size();
    }

    @Override
    public Future<Integer> getOnlineUsersAsync() {
        return userCache.sizeAsync();
    }


    /**
     * 获取聊天室最近的聊天记录
     *
     * @return {@link List} 最近的聊天记录
     * @author tianqing
     * @since 1.0
     */
    @Override
    public Collection<ChatroomMessage> getAllRecentMessages() {
        //必须指定alpha方式。所以alpha就是明确告知sort使用字节序排序，
        // 不然sort就会尝试将需要排序的数据转成double类型。
        return messageCache.range(0, -1);
    }

    @Override
    public RFuture<List<ChatroomMessage>> getAllRecentMessagesAsync() {
        return messageCache.rangeAsync(0, -1);
    }

    /**
     * 获取最近的 n 条记录
     *
     * @author tianqing
     * @since 1.0
     */
    @Override
    public Collection<ChatroomMessage> getRecentMessages(int offset, int n) {
        return messageCache.range(offset, offset + n);
    }

    @Override
    public RFuture<List<ChatroomMessage>> getRecentMessagesAsync(int offset, int n) {
        return messageCache.rangeAsync(offset, offset + n);
    }


    @Override
    public boolean addNewMessage(ChatroomMessage msg) {
        return messageCache.add(msg);
    }

    @Override
    public Future<Boolean> addNewMessageAsync(ChatroomMessage msg) {
        return messageCache.addAsync(msg);
    }

    @Override
    public int cachedMessageNumber() {
        return messageCache.size();
    }

    /**
     * 添加一个用户表示到缓存服务器；实现需要设置一个较短的过期时间
     * 当添加的用户已经存在时，不要做任何事
     *
     * @param user 聊天室的用户表示
     * @return 如果添加成功，则返回 true，否则返回 false
     * @author tianqing
     * @since 1.0
     */
    @Override
    public boolean addOnlineChatroomUser(ChatroomUser user)
            throws SnowcrystalAlreadyExistException {
        return addOnlineChatroomUser(user, false);
    }

    @Override
    public boolean addOnlineChatroomUser(ChatroomUser user, boolean overwrite)
            throws SnowcrystalAlreadyExistException {
        String key = user.getFingerprint();
        String id = user.getId();
        if (!overwrite && checkUserExistence(key, id)) {
            if (userCache.remainTimeToLive(key) > 0L) {
                log.info(userCache.remainTimeToLive(key));
                throw new SnowcrystalAlreadyExistException();
            }
        }
        try {
            userCache.put(key, user, 120L, TimeUnit.SECONDS);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 这里使用用户的设备指纹作为缓存中的 key 值来表示一个用户的一个设备
     *
     * @return 如果用户已经存在，则返回 true，否则返回 false
     */
    @Override
    public boolean checkUserExistence(String id) {
        for (ChatroomUser user : userCache.values()) {
            if (user.getId().equals(id)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 检查用户是否已经使用某个设备登录了
     *
     * @return 如果这个用户已经使用 fp 表示的设备登录，则返回 true
     */
    @Override
    public boolean checkUserExistence(String fp, String id) {
        ChatroomUser chatroomUser = userCache.get(fp);
        return chatroomUser != null && id.equals(chatroomUser.getId());
    }

    /**
     * 为匿名用户产生一个唯一标识；调用者需要在生成后
     * 检查缓存数据库中是否存在同样的标识，有则重新调用该函数生成；
     * 直到生成的标识具有唯一性。实现可以使用保证唯一性的生成算法来避免这种情况发生.
     *
     * @author tianqing
     * @since 1.0
     */
    @Override
    public String generateChatroomUserId() {
        return '#' + DigestUtils.md5DigestAsHex(UUID.randomUUID().toString().getBytes());
    }

    /**
     * 刷新用户数据的 ttl；如果成功则返回 true，否则，如果用户数据已经过期，
     * 或者用户数据不存在，返回 false
     *
     * @author tianqing
     * @since 1.0
     */
    @Override
    public boolean refreshUserExpiration(String fp) {
        return userCache.expireEntry(fp, Duration.ofSeconds(180L), Duration.ZERO);
    }

    /**
     * 检查聊天室中是否已经存在某个设备
     *
     * @param fp 设备指纹
     * @return true 如果存在，否则返回 false
     */
    @Override
    public boolean checkDeviceExistence(String fp) {
        return userCache.remainTimeToLive(fp) > 0;
    }

    /**
     * 根据设备指纹获取聊天室用户的表示，如果不存在，则返回 null
     *
     * @author tianqing
     * @since 1.0
     */
    @Override
    public ChatroomUser getChatroomUser(String fp) {
        return userCache.get(fp);
    }

    /**
     * 根据用户 id 获取所有其在聊天室的表示，每一个表示代表了这个用户的不同设备
     *
     * @param id 用户 id
     * @return 如果该指定的 id 不存在，则返回 null
     */
    @Override
    public List<ChatroomUser> retrieveChatroomUsers(String id) {
        List<ChatroomUser> users = new ArrayList<>();
        for (ChatroomUser user : userCache.values()) {
            if (user.getId().equals(id)) {
                users.add(user);
            }
        }
        return users.isEmpty() ? null : users;
    }

    @Override
    public void deleteChatroomUser(String fp) {
        userCache.remove(fp);
    }

}
